﻿using System;
using System.IO;
using System.Timers;

namespace Patriq.Common
{
    public interface IConfigFileWatcher : IDisposable
    {
        event Action OnFileChanged;
        event Action<Exception> OnError;
        void Start();
        void Stop();
    }

    public class ConfigFileWatcher : IConfigFileWatcher
    {
        private readonly FileInfo file;
        public event Action OnFileChanged;
        public event Action<Exception> OnError;
        private readonly Timer timer;
        private DateTime? lastModifiedDate;

        public ConfigFileWatcher(double checkIntervalInMilli, string fileName)
            : this(TimeSpan.FromMilliseconds(checkIntervalInMilli), new FileInfo(fileName))
        {
        }

        public ConfigFileWatcher(TimeSpan checkInterval, FileInfo file)
        {
            this.file = file;
            timer = new Timer(checkInterval.TotalMilliseconds);
            timer.Elapsed += OnTimerTick;
        }

        private void OnTimerTick(object sender, ElapsedEventArgs e)
        {
            file.Refresh();
            if (!file.Exists)
            {
                lastModifiedDate = null;
                return;
            }
            var writeDate = file.LastWriteTimeUtc;
            if (lastModifiedDate == null)
            {
                InvokeFileChangedEvent();
                lastModifiedDate = writeDate;
            }
            else if (lastModifiedDate != writeDate)
            {
                InvokeFileChangedEvent();
                lastModifiedDate = writeDate;
            }
        }

        public void Start()
        {
            if (timer.Enabled)
                return;

            lastModifiedDate = null;
            file.Refresh();
            if (file.Exists)
            {
                lastModifiedDate = file.LastWriteTimeUtc;
                try
                {
                    InvokeFileChangedEvent();
                }
                catch (Exception ex)
                {
                    InvokeErrorEvent(ex);
                }
            }
            timer.Start();
        }

        public void Stop()
        {
            timer.Stop();
        }

        private void InvokeFileChangedEvent()
        {
            var handler = OnFileChanged;
            if (handler != null)
                handler();
        }

        private void InvokeErrorEvent(Exception exception)
        {
            var handler = OnError;
            if (handler != null)
                handler(exception);
        }

        public void Dispose()
        {
            timer.Dispose();
        }
    }
}